//==================================================================================
// Draws a filled circle
//==================================================================================
void SPG_CircleFilled(SDL_Surface *Surface, Sint16 x, Sint16 y, float r, Uint32 color)
{

    if(SPG_GetAA())
        _EllipseFilledAA(Surface, x, y, r, r, color);
    else
    {
        Sint16 cx = 0;
        Sint16 cy = (Sint16)(r);
        Sint16 df = 1 - (Sint16)(r);
        Sint16 d_e = 3;
        Sint16 d_se = -2 * (Sint16)(r) + 5;

        do
        {
            if(df >= 0)
            {
                _HLine(Surface,x-cx,y+cy,x+cx,color);
                _HLine(Surface,x-cx,y-cy,x+cx,color);
            }
            
            if(cx != cy)
            {
                _HLine(Surface,x-cy,y+cx,x+cy,color);
                if(cx)
                {
                    _HLine(Surface,x-cy,y-cx,x+cy,color);
                }
            }

            if (df < 0)
            {
                df += d_e;
                d_e += 2;
                d_se += 2;
            }
            else
            {
                df += d_se;
                d_e += 2;
                d_se += 4;
                cy--;
            }
            cx++;
        }
        while (cx <= cy);
    }

}












// based on Google answer by theta-ga
void SPG_ArcFn(SDL_Surface* surface, Sint16 cx, Sint16 cy, float radius, float startAngle, float endAngle, Uint32 color, void Callback(SDL_Surface *Surf, Sint16 X, Sint16 Y, Uint32 Color))
{
    float angle;  // use as swap then use later
    
    if(startAngle > endAngle)
    {
        float swapa = endAngle;
        endAngle = startAngle;
        startAngle = swapa;
    }
    if(startAngle == endAngle)
        return;
    
    // Big angle
    if(endAngle - startAngle >= 360)
    {
        SPG_CircleFn(surface, cx, cy, radius, color, Callback);
        return;
    }
    
    // Shift together
    while(startAngle < 0 && endAngle < 0)
    {
        startAngle += 360;
        endAngle += 360;
    }
    while(startAngle > 360 && endAngle > 360)
    {
        startAngle -= 360;
        endAngle -= 360;
    }
    
    // Check if the angle to be drawn crosses 0
    SPG_bool crossesZero = (startAngle < 0 && endAngle > 0) || (startAngle < 360 && endAngle > 360);
    
    // Push all values to 0 <= angle < 360
    while(startAngle >= 360)
        startAngle -= 360;
    while(endAngle >= 360)
        endAngle -= 360;
    while(startAngle < 0)
        startAngle += 360;
    while(endAngle < 0)
        endAngle += 360;
    
    if(endAngle == 0)
        endAngle = 360;
    else if(crossesZero)
    {
        SPG_ArcFn(surface, cx, cy, radius, startAngle, 359, color, Callback);
        startAngle = 0;
    }
    
    Sint16 x = 0;
    Sint16 y = (Sint16)(radius);
    Sint16 p = 1 - (Sint16)(radius);
    Sint16 d_e = 3;
    Sint16 d_se = -2 * (Sint16)(radius) + 5;
    
    do
    {
        // Calculate the angle the current point makes with the circle center
        angle = atan2(y, x)*DEGPERRAD;
        
        // draw the circle points as long as they lie in the range specified
        
        if(x)
        {
            // draw point in range 45 to 90 degrees
            if (angle >= startAngle && angle <= endAngle) {
                Callback(surface, cx + x, cy + y, color);
            }
            // draw point in range 180 to 225 degrees
            if (270 - angle >= startAngle && 270 - angle <= endAngle) {
                Callback(surface, cx - y, cy - x, color);
            }
            // draw point in range 225 to 270 degrees
            if (angle + 180 >= startAngle && angle + 180 <= endAngle) {
                Callback(surface, cx - x, cy - y, color);
            }
            // draw point in range 315 to 360 degrees
            if (angle + 270 >= startAngle && angle + 270 <= endAngle) {
                Callback(surface, cx + y, cy - x, color);
            }
        }
        
        // draw point in range 0 to 45 degrees
        if (90 - angle >= startAngle && 90 - angle <= endAngle) {
            Callback(surface, cx + y, cy + x, color);
        }

        // draw point in range 90 to 135 degrees
        if (180 - angle >= startAngle && 180 - angle <= endAngle) {
            Callback(surface, cx - x, cy + y, color);
        }

        // draw point in range 135 to 180 degrees
        if (angle + 90 >= startAngle && angle + 90 <= endAngle) {
            Callback(surface, cx - y, cy + x, color);
        }

        // draw point in range 270 to 315 degrees
        if (360 - angle >= startAngle && 360 - angle <= endAngle) {
            Callback(surface, cx + x, cy - y, color);
        }


        x++;

        if (p < 0)
        {
            p += d_e;
            d_e += 2;
            d_se += 2;
        }
        else
        {
            p += d_se;
            d_e += 2;
            d_se += 4;
            y--;
        }
    }
    while (x <= y);
}

void SPG_Arc(SDL_Surface* surface, Sint16 x, Sint16 y, float radius, float angle1, float angle2, Uint32 color)
{
    if (SDL_MUSTLOCK(surface) && _SPG_lock)
    {
        if (SDL_LockSurface(surface) < 0)
        {
            if(_spg_useerrors)
                SPG_Error("SPG_Arc could not lock surface");
            return;
        }
    }

    SPG_ArcFn(surface, x, y, radius, angle1, angle2, color, SPG_Pixel);

    if (SDL_MUSTLOCK(surface) && _SPG_lock)
    {
        SDL_UnlockSurface(surface);
    }
}

void SPG_ArcBlend(SDL_Surface* surface, Sint16 x, Sint16 y, float radius, float angle1, float angle2, Uint32 color, Uint8 alpha)
{
    if (SDL_MUSTLOCK(surface) && _SPG_lock)
    {
        if (SDL_LockSurface(surface) < 0)
        {
            if(_spg_useerrors)
                SPG_Error("SPG_ArcBlend could not lock surface");
            return;
        }
    }

    _SPG_alpha_hack = alpha;
    SPG_ArcFn(surface, x, y, radius, angle1, angle2, color, callback_alpha_hack);

    if (SDL_MUSTLOCK(surface) && _SPG_lock)
    {
        SDL_UnlockSurface(surface);
    }
}




static inline float min(float x, float y)
{
    return (x <= y? x : y);
}
static inline float max(float x, float y)
{
    return (x >= y? x : y);
}

// Draws a pie shape from startAngle to endAngle going the quickest way, numerically
// e.g. 20 -> 210 goes positively, 180 -> 30 goes negatively, 30 -> 400 makes a full circle
void SPG_ArcFilledBlend(SDL_Surface* surf, Sint16 x, Sint16 y, float r, float startAngle, float endAngle, Uint32 color, Uint8 alpha)
{
    Sint16 cx = 0;
    Sint16 cy = (Sint16)r;
    Sint16 df = 1 - cy;
    Sint16 d_e = 3;
    Sint16 d_se = -2*cy + 5;
    Sint16 xl, xr;
    
    if(startAngle > endAngle)
    {
        float swapa = endAngle;
        endAngle = startAngle;
        startAngle = swapa;
    }
    if(startAngle == endAngle)
        return;
    
    SPG_bool reflex = endAngle - startAngle > 180;
    
    Sint16 sx = (Sint16)(r*cos(startAngle*RADPERDEG));
    Sint16 sy = (Sint16)(r*sin(startAngle*RADPERDEG));
    Sint16 ex = (Sint16)(r*cos(endAngle*RADPERDEG));
    Sint16 ey = (Sint16)(r*sin(endAngle*RADPERDEG));
    
    float sm = sy/(float)(sx);
    float em = ey/(float)(ex);
    
    // Big angle
    if(endAngle - startAngle >= 360)
    {
        SPG_CircleFilledBlend(surf, x, y, r, color, alpha);
        return;
    }
    // coordinates overlap
    if(sy == ey && sx >= ex - 1 && ex + 1 <= sx && endAngle - startAngle > 350)
    {
        SPG_CircleFilledBlend(surf, x, y, r, color, alpha);
        return;
    }
    if(sy == ey && sx == ex && endAngle - startAngle < 10)
        return;
    
    
    // Push all values to 0 <= angle < 360
    while(startAngle >= 360)
        startAngle -= 360;
    while(endAngle >= 360)
        endAngle -= 360;
    while(startAngle < 0)
        startAngle += 360;
    while(endAngle < 0)
        endAngle += 360;
    
    // I should change these to sy and ey in case the angle rounds to 180
    SPG_bool bothTop = (startAngle > 180 && endAngle > 180);
    SPG_bool bothBot = (startAngle < 180 && endAngle < 180);
    //bool bothTop = sy < 0 && ey < 0;
    //bool bothBot = sy >= 0 && ey >= 0;
    
    // Use thse to fix special cases
    SPG_bool s180 = sy == 0 && sx < 0;
    SPG_bool s0 = sy == 0 && sx >= 0;
    SPG_bool e180 = ey == 0 && ex < 0;
    SPG_bool e0 = ey == 0 && ex >= 0;
    
    
    do
    {
        if(df >= 0)
        {
            if(!bothTop)
            {
                // Draw bottom between lines
                // Intercept of lines vs edge of circle
                xl = ey > 0? (Sint16)max(cy/em + x, x - cx) : (Sint16)x - cx;
                xr = sy > 0? (Sint16)min(cy/sm + x, x + cx) : (Sint16)x + cx;
                if(xl <= xr && !(s180 && ey <= 0) && !(sy <= 0 && e0))
                    SPG_LineHBlend(surf, xl, y + cy, xr, color, alpha);
            }
            else if(reflex)
            {
                // Draw entire bottom
                SPG_LineHBlend(surf, x - cx, y + cy, x + cx, color, alpha);
                
                // Draw separated top 
                // sy <= 0, ey < 0, ex < sx
                xl = x - cx;  // Intercept of lines vs edge of circle
                xr = ey < 0? (Sint16)min(-cy/em + x, x + cx) : x + cx;
                if(xl <= xr)
                    SPG_LineHBlend(surf, xl, y - cy, xr, color, alpha);
                
                xl = sy < 0? (Sint16)max(-cy/sm + x, x - cx) : x - cx;
                xr = x + cx;
                if(xl <= xr)
                    SPG_LineHBlend(surf, xl, y - cy, xr, color, alpha);
                    
            }
            if(!bothBot)
            {
                // Draw top between lines
                xl = sy < 0? (Sint16)max(-cy/sm + x, x - cx) : (Sint16)x - cx;
                xr = ey < 0? (Sint16)min(-cy/em + x, x + cx) : (Sint16)x + cx;
                if(xl <= xr && !(e180 && sy >= 0))
                    SPG_LineHBlend(surf, xl, y - cy, xr, color, alpha);
            }
            else if(reflex)
            {
                // Draw entire top
                SPG_LineHBlend(surf, x - cx, y - cy, x + cx, color, alpha);
                
                // Draw separated bottom
                // sy >= 0, ey > 0, sx < ex
                xl = x - cx;  // Intercept of lines vs edge of circle
                xr = sy > 0? (Sint16)min(cy/sm + x, x + cx) : x + cx;
                if(xl <= xr && !s0 && !(sy > 0 && e0))
                    SPG_LineHBlend(surf, xl, y + cy, xr, color, alpha);
                
                xl = ey > 0? (Sint16)max(cy/em + x, x - cx) : x - cx;
                xr = x + cx;
                if(xl <= xr && !(sy > 0 && e0))
                    SPG_LineHBlend(surf, xl, y + cy, xr, color, alpha);
            }
        }
        if(cx != cy)
        {
            if(!bothTop)
            {
                // Draw bottom between lines
                xl = ey > 0? (Sint16)max(cx/em + x, x - cy) : (Sint16)x - cy;
                xr = sy > 0? (Sint16)min(cx/sm + x, x + cy) : (Sint16)x + cy;
                if(xl <= xr && !(s180 && ey <= 0) && !(sy <= 0 && e0))
                    SPG_LineHBlend(surf, xl, y + cx, xr, color, alpha);
            }
            else if(reflex)
            {
                if(cx != 0)
                {
                    // Draw entire bottom
                    SPG_LineHBlend(surf, x - cy, y + cx, x + cy, color, alpha);
                }
                
                // Draw separated top
                // sy <= 0, ey < 0, ex < sx
                xl = x - cy;  // Intercept of lines vs edge of circle
                xr = ey < 0? (Sint16)min(-cx/em + x, x + cy) : x + cy;
                if(xl <= xr)
                    SPG_LineHBlend(surf, xl, y - cx, xr, color, alpha);
                
                xl = sy < 0? (Sint16)max(-cx/sm + x, x - cy) : x - cy;
                xr = x + cy;
                if(xl <= xr)
                    SPG_LineHBlend(surf, xl, y - cx, xr, color, alpha);
                
            }
            if(cx && (!bothBot))  // Don't draw the center line twice
            {
                // Draw top between lines
                xl = sy < 0? (Sint16)max(-cx/sm + x, x - cy) : (Sint16)x - cy;
                xr = ey < 0? (Sint16)min(-cx/em + x, x + cy) : (Sint16)x + cy;
                if(xl <= xr && !(e180 && sy >= 0))
                    SPG_LineHBlend(surf, xl, y - cx, xr, color, alpha);
            }
            else if(bothBot && reflex)
            {
                if(cx != 0)
                {
                    // Draw entire top
                    SPG_LineHBlend(surf, x - cy, y - cx, x + cy, color, alpha);
                }
                
                // Draw separated bottom
                // sy >= 0, ey > 0, sx < ex
                xl = x - cy;  // Intercept of lines vs edge of circle
                xr = sy > 0? (Sint16)min(cx/sm + x, x + cy) : x + cy;
                if(xl <= xr && !s0 && !(sy > 0 && e0))
                    SPG_LineHBlend(surf, xl, y + cx, xr, color, alpha);
                
                xl = ey > 0? (Sint16)max(cx/em + x, x - cy) : x - cy;
                xr = x + cy;
                if(xl <= xr && !(sy > 0 && e0))
                    SPG_LineHBlend(surf, xl, y + cx, xr, color, alpha);
                
            }
                
        }
        if(df < 0)
        {
            df += d_e;
            d_e += 2;
            d_se += 2;
        }
        else
        {
            df += d_se;
            d_e += 2;
            d_se += 4;
            cy--;
        }
        cx++;
    }while(cx <= cy);
    
}

void SPG_ArcFilled(SDL_Surface* surface, Sint16 cx, Sint16 cy, float radius, float startAngle, float endAngle, Uint32 color)
{
    SPG_ArcFilledBlend(surface, cx, cy, radius, startAngle, endAngle, color, SDL_ALPHA_OPAQUE);
}